
/**
\file Definition of nmn::math::vector.
\author Sean Tromans
*/

#include <boost/preprocessor.hpp>
#include <boost/numeric/conversion/cast.hpp>
#include <iterator>

#define NMN_MATH_VECTOR_SET_ARRAY( z, n, _ )	m_array[n] = boost::numeric_cast<value_type>( BOOST_PP_CAT( p, n ) );

#define NMN_MATH_VECTOR_CONSTRUCTOR( z, n, _ )	\
	template < typename TValueType, size_t Size >	\
	template < BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(n), typename T) >	\
	inline vector< TValueType, Size >::vector( BOOST_PP_ENUM_BINARY_PARAMS_Z(z, BOOST_PP_INC(n), T, const & p) ) \
	{	\
		static_assert( static_size == BOOST_PP_INC(n), "Invalid number of parameters." );	\
		BOOST_PP_CAT( BOOST_PP_REPEAT_, z )( BOOST_PP_INC(n), NMN_MATH_VECTOR_SET_ARRAY, _ );	\
	}

namespace nmn
{

	namespace math
	{

		/**
		Default Constructor. Does nothing.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline vector< TValueType, Size >::vector( )
		{ }

		/**
		Initialise all components of the vector to the same value.
		@param value The value to initialise each component to.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		template < typename TOtherValueType >
		inline vector< TValueType, Size >::vector( TOtherValueType const & value )
		{
			m_array.fill( boost::numeric_cast< value_type >( value ) );
		}

		/**
		Copy construct from another vector of the same size, but potentially a different type.
		@param rhs The vector to copy.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		template < typename TOtherValueType >
		inline vector< TValueType, Size >::vector( typename vector< TOtherValueType, Size > const & rhs )
			:	m_array( rhs.m_array )
		{
		}

		/**
		Initialise a vector from array of values.
		@param values The array containing the values to initialise from.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		template < typename TOtherValueType >
		inline vector< TValueType, Size >::vector( boost::array< TOtherValueType, Size > const & values )
			:	m_array( values )
		{
		}

		BOOST_PP_REPEAT_FROM_TO( 1, NMN_MATH_VECTOR_MAX_ARITY, NMN_MATH_VECTOR_CONSTRUCTOR, 0 );

		/**
		Assign this vector to be equal to another.
		@param rhs The vector to copy.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		template < typename TOtherValueType >
		inline typename vector< TValueType, Size >::my_type & vector< TValueType, Size >::operator = ( typename vector< TOtherValueType, Size > const & rhs )
		{
			m_array = rhs.m_array;
			return *this;
		}

		/**
		The size of the vector.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline typename vector< TValueType, Size >::size_type vector< TValueType, Size >::size( ) const
		{
			return static_size;
		}

		/**
		Component wise addition of another vector onto this.
		@param rhs The vector to be added.
		@return A reference to this after the addition.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline 
			typename vector< TValueType, Size >::my_type &
				vector< TValueType, Size >::operator += ( typename vector< TValueType, Size >::my_type const & rhs )
		{
			for ( size_type i = 0; i < size( ); ++i )
			{
				at( i ) += rhs.at( i );
			}

			return *this;
		}

		/**
		Component wise subtraction of another vector onto this.
		@param rhs The vector to be subtracted.
		@return A reference to this after the subtraction.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline 
			typename vector< TValueType, Size >::my_type &
				vector< TValueType, Size >::operator -= ( typename vector< TValueType, Size >::my_type const & rhs )
		{
			for ( size_type i = 0; i < size( ); ++i )
			{
				at( i ) -= rhs.at( i );
			}

			return *this;
		}

		/**
		Component wise multiplication of another vector onto this.
		@param rhs The vector to be multiplied.
		@return A reference to this after the multiplication.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline 
			typename vector< TValueType, Size >::my_type &
				vector< TValueType, Size >::operator *= ( typename vector< TValueType, Size >::my_type const & rhs )
		{
			for ( size_type i = 0; i < size( ); ++i )
			{
				at( i ) *= rhs.at( i );
			}

			return *this;
		}

		/**
		Component wise multiplication of a value type onto this.
		@param rhs The value type to be multiplied.
		@return A reference to this after the multiplication.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline 
			typename vector< TValueType, Size >::my_type &
				vector< TValueType, Size >::operator *= ( typename vector< TValueType, Size >::value_type const & rhs )
		{
			for ( size_type i = 0; i < size( ); ++i )
			{
				at( i ) *= rhs;
			}

			return *this;
		}

		/**
		Component wise division of a value type onto this.
		@param rhs The value type to be divided.
		@return A reference to this after the division.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline 
			typename vector< TValueType, Size >::my_type &
				vector< TValueType, Size >::operator /= ( typename vector< TValueType, Size >::value_type const & rhs )
		{
			// TODO: NMN_ASSERT( nmn::math::abs( rhs ) > nmn::math::constants< value_type >::epsilon( ) );

			for ( size_type i = 0; i < size( ); ++i )
			{
				at( i ) /= rhs;
			}

			return *this;
		}

		/**
		Component wise strict equality check against another vector.
		@param rhs The vector to compare against.
		@return True if each corresponding component in the two vectors are exactly equal.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline bool vector< TValueType, Size >::strict_equals( typename vector< TValueType, Size >::my_type const & rhs ) const
		{
			return m_array == rhs.m_array;
			////	Fine VS2010, if you really want to ignore your warning being disabled, I'll just not use std::equals. Jerk.
			//auto end1 = end( );
			//for ( auto begin1 = begin( ), begin2 = rhs.begin( ); begin1 != end1; ++begin1, ++begin2 )
			//{
			//	if ( (*begin1) != (*begin2) )
			//	{
			//		return false;
			//	}
			//}

			//return true;
		}

		/**
		Gets a component from inside the vector.
		@param index The index of the component to return.
		@return a reference to the component at \c index.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline
			typename vector< TValueType, Size >::reference_type
				vector< TValueType, Size >::operator[] ( typename vector< TValueType, Size >::size_type index )
		{
			return at( index );
		}

		/**
		Gets a component from inside the vector.
		@param index The index of the component to return.
		@return a constant reference to the component at \c index.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline
			typename vector< TValueType, Size >::const_reference_type
				vector< TValueType, Size >::operator[] ( typename vector< TValueType, Size >::size_type index ) const
		{
			return at( index );
		}

		/**
		Gets a component from inside the vector.
		@param index The index of the component to return.
		@return a reference to the component at \c index.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline
			typename vector< TValueType, Size >::reference_type
				vector< TValueType, Size >::at( typename vector< TValueType, Size >::size_type index )
		{
			return m_array.at( index );
		}

		/**
		Gets a component from inside the vector.
		@param index The index of the component to return.
		@return a constant reference to the component at \c index.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline
			typename vector< TValueType, Size >::const_reference_type
				vector< TValueType, Size >::at( typename vector< TValueType, Size >::size_type index ) const
		{
			return m_array.at( index );
		}

		/**
		Returns an iterator to the first element in the vector.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline
			typename vector< TValueType, Size >::iterator
				vector< TValueType, Size >::begin( )
		{
			return m_array.begin( );
		}

		/**
		Returns a delimiting iterator to the vector.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline
			typename vector< TValueType, Size >::iterator
				vector< TValueType, Size >::end( )
		{
			return m_array.end( );
		}

		/**
		Returns a constant iterator to the first element in the vector.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline
			typename vector< TValueType, Size >::const_iterator
				vector< TValueType, Size >::begin( ) const
		{
			return cbegin( );
		}

		/**
		Returns a delimiting constant iterator to the vector.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline
			typename vector< TValueType, Size >::const_iterator
				vector< TValueType, Size >::end( ) const
		{
			return cend( );
		}

		/**
		Returns a constant iterator to the first element in the vector.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline
			typename vector< TValueType, Size >::const_iterator
				vector< TValueType, Size >::cbegin( ) const
		{
			return m_array.begin( );
		}

		/**
		Returns a delimiting constant iterator to the vector.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline
			typename vector< TValueType, Size >::const_iterator
				vector< TValueType, Size >::cend( ) const		
		{
			return m_array.end( );
		}

		/**
		Returns a reverse iterator to the last element in the vector.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline
			typename vector< TValueType, Size >::reverse_iterator
				vector< TValueType, Size >::rbegin( )
		{
			return m_array.rbegin( );
		}

		/**
		Returns a delimiting reverse iterator to the vector.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline
			typename vector< TValueType, Size >::reverse_iterator
				vector< TValueType, Size >::rend( )
		{
			return m_array.rend( );
		}

		/**
		Returns a constant reverse iterator to the last element in the vector.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline
			typename vector< TValueType, Size >::const_reverse_iterator
				vector< TValueType, Size >::rbegin( ) const
		{
			return m_array.rbegin( );
		}

		/**
		Returns a delimiting constant reverse iterator to the vector.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline
			typename vector< TValueType, Size >::const_reverse_iterator
				vector< TValueType, Size >::rend( ) const
		{
			return m_array.rend( );
		}

		/**
		Swaps this vector with another.
		@param rhs The vector to swap with.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline void vector< TValueType, Size >::swap( typename vector< TValueType, Size >::my_type & rhs )
		{
			m_array.swap( rhs.m_array );
		}

		/**
		Retreives the raw data contained within the vector.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline
			typename vector< TValueType, Size >::value_type const *
				vector< TValueType, Size >::data( ) const
		{
			return m_array.data( );
		}

		/**
		\relates nmn::math::vector
		Outputs a vector2 to an output stream.
		@param os The output ostream.
		@param vec The vector to output.
		@return A reference to the output stream.
		\author Sean Tromans
		*/
		template < typename Elem, typename Traits, typename TValueType, size_t Size >
		inline std::basic_ostream< Elem, Traits > & operator << ( std::basic_ostream< Elem, Traits > & os, vector< TValueType, Size > const & vec )
		{
			os << "{ ";
			std::copy(	vec.begin(), vec.end(), std::ostream_iterator< TValueType >( os, " " ) );
			return os << "}";
		}

		/**
		Component wise absolute equality check.
		@param lhs The first vector to compare.
		@param rhs The second vector to compare.
		@return true if the two vectors are exactly equal.
		\author Sean Tromans
		*/
		template < typename TValueType, size_t Size >
		inline bool operator == ( vector< TValueType, Size > const & lhs, vector< TValueType, Size > const & rhs )
		{
			return lhs.strict_equals( rhs );
		}

	};

};

namespace std
{
	/**
	Swaps the values of two vectors.
	@param lhs The first vector.
	@param rhs The second vector.
	\author Sean Tromans
	*/
	template < typename TValueType, size_t Size >
	inline void swap( nmn::math::vector< TValueType, Size > & lhs, nmn::math::vector< TValueType, Size > & rhs )
	{
		lhs.swap( rhs );
	}
}

#undef	NMN_MATH_VECTOR_CONSTRUCTOR
#undef	NMN_MATH_VECTOR_SET_ARRAY
